package application.anno.impl;

import application.anno.AutoClose;
import application.anno.AutoRegister;
import application.anno.MessageActions;
import application.anno.Unregister;
import application.exception.NotFoundNecessaryAnnotationException;
import application.manager.annotationManager.AutoCloseManager;
import application.messager.MessageReceiver;
import application.util.ReflectionsUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class AutoRegisterImpl {

    public void registerByObject(Object o) throws NotFoundNecessaryAnnotationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        if (o.getClass().getAnnotation(Unregister.class) != null) {
            return;
        }
        List<Field> declaredFields = Arrays.asList(o.getClass().getDeclaredFields());
        Set<Field> annotationByField = ReflectionsUtil.getAnnotationByField(AutoRegister.class);
        if (annotationByField == null) {
            return;
        }
        annotationByField.retainAll(declaredFields);
        for (Field field : annotationByField) {
            field.setAccessible(true);
            Object object = field.get(o);
            AutoRegister annotation = field.getAnnotation(AutoRegister.class);
            if (annotation == null) {
                annotation = object.getClass().getSuperclass().getDeclaredAnnotation(AutoRegister.class);
            }
            Method declaredMethod = getMethod(object.getClass(), annotation);
            declaredMethod.setAccessible(true);
            declaredMethod.invoke(object);
            if (!isExistAutoClose(object.getClass(), AutoClose.class)) {
                throw new NotFoundNecessaryAnnotationException("Not found AutoClose annotation");
            }
            AutoCloseManager.getInstance().getAutoCloses().put(object.getClass(), object);
        }
    }

    public void registerListObject(List<?> objects) throws NotFoundNecessaryAnnotationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        for (Object o : objects) {
            registerByObject(o);
        }
    }

    public void registerClass(Class<?> cls) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NotFoundNecessaryAnnotationException {
        if (cls.getAnnotation(Unregister.class) != null) {
            return;
        }
        try {
            Object object = cls.newInstance();
        AutoRegister autoRegister = cls.getAnnotation(AutoRegister.class);
        if (autoRegister == null) {
            autoRegister = cls.getSuperclass().getAnnotation(AutoRegister.class);
        }
        Method declaredMethod = getMethod(cls, autoRegister);
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(object);
        if (!isExistAutoClose(object.getClass(), AutoClose.class)) {
            throw new NotFoundNecessaryAnnotationException("Not found AutoClose annotation");
        }
        AutoCloseManager.getInstance().getAutoCloses().put(object.getClass(), object);
        }catch (Exception e){

        }
    }

    public void registerListClass() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NotFoundNecessaryAnnotationException {
        Set<Class<?>> classes = ReflectionsUtil.getAnnotationByClass(AutoRegister.class, false);
        for (Class<?> cls : classes) {
            registerClass(cls);
        }
    }

    public boolean isExistAutoClose(Class<?> cls, Class<AutoClose> closeClass) throws NoSuchMethodException {
        return cls.getAnnotation(closeClass) != null || cls.getSuperclass().getAnnotation(closeClass) != null;
    }

    public Method getMethod(Class<?> cls, AutoRegister autoRegister) throws NoSuchMethodException {
        for (Method method : cls.getDeclaredMethods()) {
            if (autoRegister.methodName().equals(method.getName())) {
                return method;
            }
        }
        return cls.getSuperclass().getDeclaredMethod(autoRegister.methodName());
    }

    public void registerInMessageReceiver(MessageReceiver receiver) throws NotFoundNecessaryAnnotationException {
        MessageActions declaredAnnotation = receiver.getClass().getDeclaredAnnotation(MessageActions.class);
        if (declaredAnnotation == null) {
            throw new NotFoundNecessaryAnnotationException("Not found MessageAction annotation");
        }
        String[] actions = declaredAnnotation.actions();
        for (String action : actions) {
            receiver.addAction(action);
        }
        receiver.register();
        AutoCloseManager.getInstance().getAutoCloses().put(receiver.getClass(), receiver);
    }
}
